/** 
 * @Description: 模型编辑器类，用于在Cesium场景中添加、编辑、移动和旋转模型
 * @Author: 程前
 * @Date: 2024-12-11 16:54:22
 * @LastEditors: 程前
 * @LastEditTime: 2025-01-24 15:46:55
 */

import {
    Viewer,
    ScreenSpaceEventHandler,
    Cartesian2,
    Cartesian3,
    ScreenSpaceEventType,
    HeadingPitchRoll,
    Transforms,
    LabelStyle,
    Color,
    VerticalOrigin,
    HorizontalOrigin,
    DistanceDisplayCondition,
    Model,
    Ellipsoid,
    Matrix4,
    BillboardCollection,
    Billboard,
    PrimitiveCollection,
    LabelCollection,
    Label,
    Entity,
    NearFarScalar,
    Math as CesiumMath,
    HeightReference
} from "cesium"
import { setEventByHandler, enableCameraControl, disableCameraControl, getAssetsFile } from "@/utils"
import { ModelConfig } from "@/types/pinia/cesium-edit";
import { useCesiumEditStore } from "@/store/modules/cesium-edit";
import { watch, watchEffect } from "vue";

type ModelPlottingOptions = {
    modelUrl: string;  // 模型URL
    handler: ScreenSpaceEventHandler;
    scale?: number;    // 模型缩放比例
    minDistance?: number;  // 最小显示距离
    label?: string;    // 添加标签文本选项
}

// 实体属性
interface MapModelEntity {
    id: string;
    src: string;
    position: Cartesian3;
    label: string;
    labelColor: string;
    scale: number;
    pixel: number;
    rotation: number;
}

export default class CesiumModel {
    private viewer: Viewer;
    private handler: ScreenSpaceEventHandler;
    private modelUrl: string = '';
    private coverUrl: string = "images/models/J-15.png"
    private minDistance: number = 100000;
    private minLabelDistance: number = 20000;
    private primitives: PrimitiveCollection; // 所有图元集合
    private billboardsCollection: BillboardCollection; // 广告牌集合
    private labelsCollection: LabelCollection; // 纯文本广告牌集合
    private previewModel: Model | null = null; // 当前预览模型
    private previewLabelBillboard: Billboard | null = null;  // 当前预览模型的广告牌
    private previewBillboard: Billboard | null = null; // 当前预览模型的略缩图广告牌
    private previewLabel: Label | null = null; // 当前预览的纯文本标签-暂存
    private models: Model[] = [];  // 添加模型数组-暂
    private modelBillboards: Billboard[] = [];  // 创建的模型略图广告牌-暂存
    private labelBillboards: Billboard[] = [];  // 创建的默地向广告牌-暂存
    private modelLabels: Label[] = [];  // 当前创建的纯文本标签-暂存
    private currentConfig: ModelConfig;
    private entityProperties: Map<Entity, MapModelEntity> = new Map(); // todo
    private cesiumEditStore = useCesiumEditStore()
    private watchList: Function[] = []

    constructor(viewer: Viewer, handler: ScreenSpaceEventHandler) {
        this.viewer = viewer;
        this.handler = handler;
        this.currentConfig = this.cesiumEditStore.model.config
        this.modelUrl = this.cesiumEditStore.model.src;
        // this.coverUrl = this.cesiumEditStore.model.coverSrc;
        this.primitives = this.viewer.scene.primitives;
        this.billboardsCollection = new BillboardCollection();
        this.primitives.add(this.billboardsCollection);
        this.labelsCollection = new LabelCollection();
        this.primitives.add(this.labelsCollection);
    }

    /**
     * 创建模型
     */
    private async createModel(position: Cartesian3): Promise<Model> {
        // 计算模型在地面上的朝向
        const hpr = new HeadingPitchRoll(CesiumMath.toRadians(this.currentConfig.rotation), 0, 0);
        const modelMatrix = Transforms.headingPitchRollToFixedFrame(
            position,
            hpr,
            Ellipsoid.WGS84,
            Transforms.eastNorthUpToFixedFrame
        );
        // Matrix4.multiplyByUniformScale(modelMatrix, this.currentConfig.scale, modelMatrix);

        return await Model.fromGltfAsync({
            url: this.modelUrl,
            modelMatrix: modelMatrix,
            scene: this.viewer.scene,
            scale: this.currentConfig.scale, // 参照恒歌 默认50
            maximumScale: 256, // 参照恒歌 最大256
            minimumPixelSize: this.currentConfig.pixel,
            distanceDisplayCondition: new DistanceDisplayCondition(0, this.minDistance),
            heightReference: HeightReference.CLAMP_TO_GROUND

        });
    }

    /**
     * 创建广告牌
     */
    private createBillboard(position: Cartesian3): Billboard {
        return this.billboardsCollection.add({
            image: getAssetsFile(this.coverUrl), //暂时写死
            position: position,
            verticalOrigin: VerticalOrigin.CENTER,
            horizontalOrigin: HorizontalOrigin.CENTER,
            scale: 1.0,
            distanceDisplayCondition: new DistanceDisplayCondition(this.minDistance, Number.MAX_VALUE)
        });
    }

    /**
     * 生成标签图片
     */
    private generateLabelImage(text: string, color: string): string {
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        if (!context) return '';

        canvas.width = 400;
        canvas.height = 70;

        context.clearRect(0, 0, canvas.width, canvas.height);

        // 使用半透明背景
        context.fillStyle = 'rgba(0, 0, 0, 0.6)';
        context.roundRect(0, 0, canvas.width, canvas.height, 10);
        context.fill();

        context.font = '32px Microsoft YaHei';
        context.fillStyle = color;
        context.textAlign = 'center';
        context.textBaseline = 'middle';
        context.fillText(text, canvas.width / 2, canvas.height / 2);

        return canvas.toDataURL();
    }

    /**
     * 创建纯文本标签
     */
    private createLabel(position: Cartesian3): Label {
        return this.labelsCollection.add({
            position: position,
            text: this.currentConfig.label,
            font: '24px Microsoft YaHei',
            fillColor: Color.fromCssColorString(this.currentConfig.labelColor),
            outlineWidth: 0,
            style: LabelStyle.FILL_AND_OUTLINE,
            verticalOrigin: VerticalOrigin.BOTTOM,
            horizontalOrigin: HorizontalOrigin.CENTER,
            pixelOffset: new Cartesian2(0, -20),
            scale: 1.0,
            distanceDisplayCondition: new DistanceDisplayCondition(this.minLabelDistance, Number.MAX_VALUE),  //参照恒歌 20km外显示纯文本
            disableDepthTestDistance: Number.POSITIVE_INFINITY
        });
    }

    /**
     * 创建标签广告牌
     */
    private createLabelBillboard(position: Cartesian3): Billboard {
        const config = this.currentConfig
        return this.billboardsCollection.add({
            image: this.generateLabelImage(config.label, config.labelColor),
            position: position,
            verticalOrigin: VerticalOrigin.BOTTOM,
            horizontalOrigin: HorizontalOrigin.CENTER,
            scale: 1,
            pixelOffset: new Cartesian2(0, -70),
            distanceDisplayCondition: new DistanceDisplayCondition(0, this.minLabelDistance),
            // translucencyByDistance: new NearFarScalar(1000, 1.0, 5000, 0.8),
            scaleByDistance: new NearFarScalar(1000, 1.0, 5000, 0.6),
            disableDepthTestDistance: Number.POSITIVE_INFINITY
        });
    }

    /**
     * 初始化事件监听
     */
    async initEvents(): Promise<void> {
        // 先加载预览模型
        const initialPosition = this.viewer.scene.camera.pickEllipsoid(
            new Cartesian2(0, 0),
            this.viewer.scene.globe.ellipsoid
        );

        if (initialPosition) {
            this.previewModel = await this.createModel(initialPosition);
            this.previewModel.color = Color.WHITE.withAlpha(0.5);
            this.primitives.add(this.previewModel);
            this.previewBillboard = this.createBillboard(initialPosition);
            this.previewBillboard.color = Color.WHITE.withAlpha(0.5);
            // 确保有标签文本时才创建标签
            if (this.currentConfig.label && this.currentConfig.label.trim() !== '') {
                // 预览广告标签
                this.previewLabelBillboard = this.createLabelBillboard(initialPosition);
                this.previewLabelBillboard.color = Color.WHITE.withAlpha(0.5);
                // 预览纯文本标签
                this.previewLabel = this.createLabel(initialPosition);
                this.previewLabel.fillColor = Color.WHITE.withAlpha(0.5);
            }
        }

        // 模型加载完成后再添加鼠标移动事件
        const mouseMove = async (event: any) => {
            const position = this.viewer.scene.camera.pickEllipsoid(
                event.endPosition,
                this.viewer.scene.globe.ellipsoid
            );

            if (position && this.previewModel) {
                // 更新位置和朝向
                const hpr = new HeadingPitchRoll(CesiumMath.toRadians(this.currentConfig.rotation), 0, 0);
                const modelMatrix = Transforms.headingPitchRollToFixedFrame(
                    position,
                    hpr,
                    Ellipsoid.WGS84,
                    Transforms.eastNorthUpToFixedFrame
                );
                this.previewModel.modelMatrix = modelMatrix;

                if (this.previewBillboard) {
                    this.previewBillboard.position = position;
                }
                if (this.previewLabelBillboard) {
                    this.previewLabelBillboard.position = position;
                }
                if (this.previewLabel) {
                    this.previewLabel.position = position;
                }
            }
        }
        setEventByHandler(this.handler, mouseMove, ScreenSpaceEventType.MOUSE_MOVE);

        const leftClick = async (event: any) => {
            const position = this.viewer.scene.camera.pickEllipsoid(
                event.position,
                this.viewer.scene.globe.ellipsoid
            );
            if (position) {
                this.initWatcher()
                const model = await this.createModel(position);
                this.primitives.add(model);
                this.models.push(model);

                const billboard = this.createBillboard(position);
                this.modelBillboards.push(billboard);

                // 创建两种标签
                if (this.currentConfig.label && this.currentConfig.label.trim() !== '') {
                    const labelBillboard = this.createLabelBillboard(position);
                    this.labelBillboards.push(labelBillboard);
                    const textLabel = this.createLabel(position);
                    this.modelLabels.push(textLabel);
                }
            }
        }
        setEventByHandler(this.handler, leftClick, ScreenSpaceEventType.LEFT_CLICK);
    }

    /**
     * 销毁编辑器
     */
    destroy(): void {
        if (this.handler) {
            this.handler.destroy();
        }
        // 只移除预览模型和广告牌
        if (this.previewModel) {
            this.primitives.remove(this.previewModel);
            this.previewModel = null;
        }
        if (this.previewBillboard) {
            this.billboardsCollection.remove(this.previewBillboard);
            this.previewBillboard = null;
        }
        if (this.previewLabelBillboard) {
            this.billboardsCollection.remove(this.previewLabelBillboard);
            this.previewLabelBillboard = null;
        }
        if (this.previewLabel) {
            this.labelsCollection.remove(this.previewLabel);
            this.previewLabel = null;
        }
        this.watchList.forEach(unWatch => unWatch());
    }

    /**
     * 设置模型缩放比例
     * @param scale 缩放比例
     */
    setScale(scale: number): void {
        this.previewModel.scale = scale
    }

    /**
     * 设置模型显示距离
     * @param distance 显示距离（米）
     */
    setMinDistance(distance: number): void {
        this.minDistance = distance;
        // 更新预览模型
        if (this.previewModel) {
            this.previewModel.distanceDisplayCondition = new DistanceDisplayCondition(0, distance);
        }
        if (this.previewBillboard) {
            this.previewBillboard.distanceDisplayCondition = new DistanceDisplayCondition(distance, Number.MAX_VALUE);
        }
        if (this.previewLabelBillboard) {
            this.previewLabelBillboard.distanceDisplayCondition = new DistanceDisplayCondition(0, distance);
        }
        // 更新已创建的模型
        this.models.forEach(model => {
            model.distanceDisplayCondition = new DistanceDisplayCondition(0, distance);
        });
        this.modelBillboards.forEach(billboard => {
            billboard.distanceDisplayCondition = new DistanceDisplayCondition(distance, Number.MAX_VALUE);
        });
        this.labelBillboards.forEach(billboard => {
            billboard.distanceDisplayCondition = new DistanceDisplayCondition(0, distance);
        });
    }

    /**
     * 设置模型旋转角度
     * @param heading 航向角（弧度）
     * @param pitch 俯仰角（弧度）
     * @param roll 翻滚角（弧度）
     */
    setRotation(heading: number, pitch: number, roll: number): void {
        const rotation = new HeadingPitchRoll(CesiumMath.toRadians(heading), pitch, roll);
        // 更新预览模型
        if (this.previewModel && this.previewModel.modelMatrix) {
            const position = Matrix4.getTranslation(this.previewModel.modelMatrix, new Cartesian3());
            const modelMatrix = Transforms.headingPitchRollToFixedFrame(
                position,
                rotation,
                Ellipsoid.WGS84,
                Transforms.eastNorthUpToFixedFrame
            );
            this.previewModel.modelMatrix = modelMatrix;
        }
    }

    /**
     * 设置模型大小（像素）
     * @param size 最小像素大小
     */
    setMinPixelSize(size: number): void {
        this.previewModel.minimumPixelSize = size;
    }

    /**
     * 更新标签文本
     */
    updateLabel(text: string, color: string): void {
        const labelImage = this.generateLabelImage(text, color);
        // 更新预览广告标签
        if (this.previewLabelBillboard) {
            this.previewLabelBillboard.image = labelImage;
        }
        // 更新预览纯文本标签
        if (this.previewLabel) {
            this.previewLabel.text = text;
            this.previewLabel.fillColor = Color.fromCssColorString(color)
        }
    }


    /**
     * 监听配置变化
     */
    private initWatcher() {
        const labelUnWatch = watchEffect(() => {
            this.updateLabel(this.currentConfig.label, this.currentConfig.labelColor)
        })
        this.watchList.push(labelUnWatch)
        const pixelUnWatch = watch(() => this.currentConfig.pixel, (newVal: number) => {
            this.previewModel.minimumPixelSize = newVal;
        })
        this.watchList.push(pixelUnWatch)
        const scaleUnWatch = watch(() => this.currentConfig.scale, (newVal: number) => {
            this.previewModel.scale = newVal
        })
        this.watchList.push(scaleUnWatch)
        const rotationUnWatch = watch(() => this.currentConfig.rotation, (newVal: number) => {
            this.setRotation(newVal, 0, 0)
        })
        this.watchList.push(rotationUnWatch)

    }
} 